package csv_parser;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner;

import csv_parser.InstancedParser.ClassificationResult.Stats;
import neural_network.CommandRunner;

public class InstancedParser
{
	public static String OCTAVE_PATH = "octave";
	public static String SCRIPTS_BASE = "octave_scripts" + File.separator;
	
	private float[] max = new float[6];
	private float[][] matrix = new float[1050][6];
	private int[] types = new int[1050];
	private float[] mean = new float[6];
	// d_max 37
	// q_d_max 38
	// alpha_f_min 2
	// f_min 64
	// alpha_f_max 100
	// f_max 81
	private float[] importance_factor = new float[6];
	private float[] std = new float[6];
	private float[] min = new float[6];
	private int matrix_i = 0;
	private int count = 0;

	
	public InstancedParser()
	{
		for (int i = 0; i < min.length; i++)
			min[i] = Float.MAX_VALUE;

		importance_factor[0] = 1f;
		importance_factor[1] = 1f;
		importance_factor[2] = 1.1f;
		importance_factor[3] = 1f;
		importance_factor[4] = 0.1f;
		importance_factor[5] = 0.25f;
	}
	
	public void doPolynomial(String fileName, int type, ArrayList<String> data,
			ArrayList<String> plottableData) {
		Scanner sc;

		try {
			sc = new Scanner(new File(fileName));
			boolean start = false;
			int j = 0;
			float d_max = 0;
			float f_max = 0;
			float alpha_f_max = 0;
			float q_d_max = 0;
			float f_min = Float.MAX_VALUE;
			float alpha_f_min = 0;
			int size = 0;

			while (sc.hasNext() && size < 350) {
				String rawLine = sc.nextLine();
				if(rawLine.trim().length() == 0) continue;
				String[] line = rawLine.split(";"); //sc.nextLine().split(";");
				if (!start)
				{
					if(line.length > 0 && line[0].startsWith("-10")) 
					{
						start = true;
						d_max = 0;
						f_max = 0;
						alpha_f_max = 0;
						q_d_max = 0;
						f_min = Float.MAX_VALUE;
						alpha_f_min = 0;
						j = 0;
					}
					else
					{
						//System.out.println("line[] lengith = " + line.length);
						//if(line.length > 0) System.out.println("First chunk = \"" + line[0] + "\"");
						//System.out.println("Skipping over line: " + rawLine);
						continue;
					}
				}
				float[] nums = new float[6];
				if (j < 84 && start) {
					for (int i = 0; i < 6; i++) {
						if (!line[i].equals("?")) {
							String temp = line[i].replace(",", ".");
							float num = Float.parseFloat(temp);
							nums[i] = num;
							if (i == 1 && num > d_max) {
								d_max = num;
								q_d_max = nums[0];
							}

							if (i == 5) {
								if (num < f_min) {
									f_min = num;
									alpha_f_min = nums[4];
								}
								if (num > f_max) {
									f_max = num;
									alpha_f_max = nums[4];
								}
							}

						}
					}
				}
				if (start && line[0].equals("10,75")) {
					count++;
					//StringBuilder builder = new StringBuilder();
					matrix[matrix_i][0] = d_max;
					matrix[matrix_i][1] = q_d_max;
					matrix[matrix_i][2] = f_min;
					matrix[matrix_i][3] = alpha_f_min;
					matrix[matrix_i][4] = f_max;
					matrix[matrix_i][5] = alpha_f_max;

					mean[0] += d_max;
					mean[1] += q_d_max;
					mean[2] += f_min;
					mean[3] += alpha_f_min;
					mean[4] += f_max;
					mean[5] += alpha_f_max;

					min[0] = Math.min(min[0], d_max);
					min[1] = Math.min(min[1], q_d_max);
					min[2] = Math.min(min[2], f_min);
					min[3] = Math.min(min[3], alpha_f_min);
					min[4] = Math.min(min[4], f_max);
					min[5] = Math.min(min[5], alpha_f_max);

					max[0] = Math.max(max[0], d_max);
					max[1] = Math.max(max[1], q_d_max);
					max[2] = Math.max(max[2], f_min);
					max[3] = Math.max(max[3], alpha_f_min);
					max[4] = Math.max(max[4], f_max);
					max[5] = Math.max(max[5], alpha_f_max);

					types[matrix_i] = type;
				
					start = false;
			
					matrix_i++;
					size++;
				}
				j++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
	
	private static boolean doPolynomialForOne(String fileName, float[] data) 
	{
		try
		{
			Scanner s = new Scanner(new File(fileName));
			return doPolynomialForOne(s, data);
		} 
		catch (FileNotFoundException e)
		{
			return false;
		}
	}
	
	private static boolean doPolynomialForOne(Scanner sc, float[] data) 
	{
		boolean start = false;
		int j = 0;
		float d_max = 0;
		float f_max = 0;
		float alpha_f_max = 0;
		float q_d_max = 0;
		float f_min = Float.MAX_VALUE;
		float alpha_f_min = 0;
		int size = 0;
	
		while (sc.hasNext() && size < 1) 
		{
			String wholeLine = sc.nextLine();
			
			if(wholeLine.trim().length() == 0) continue;
			if(!Character.isDigit(wholeLine.charAt(0)) && wholeLine.charAt(0) != '-') continue;
			
			//System.out.println("POLY> " + wholeLine);
			
			String[] line = wholeLine.split(";");
			if (!start && line.length > 0 && (line[0].equals("-10") || line[0].equals("-10.0"))) {
				//System.out.println("START = TRUE");
				start = true;
				d_max = 0;
				f_max = 0;
				alpha_f_max = 0;
				q_d_max = 0;
				f_min = Float.MAX_VALUE;
				alpha_f_min = 0;
				j = 0;
			}
			float[] nums = new float[6];
			if (j < 84 && start) 
			{
				for (int i = 0; i < 6; i++) 
				{
					if (!line[i].equals("?") && !line[i].equals("NaN")) 
					{
						//System.out.println("PARSE " + i);
						
						String temp = line[i].replace(',', '.');
						float num = 0.0f;
						try
						{
							num = Float.parseFloat(temp);
						}
						catch (Exception e)
						{
							System.out.println("FAIL to float \"" + temp + "\"");
						}
						nums[i] = num;
						if (i == 1 && num > d_max) {
							d_max = num;
							q_d_max = nums[0];
						}
	
						if (i == 5) {
							if (num < f_min) {
								f_min = num;
								alpha_f_min = nums[4];
							}
							if (num > f_max) {
								f_max = num;
								alpha_f_max = nums[4];
							}
						}
	
					}
				}
			}
			if (start && (line[0].equals("10,75") || line[0].equals("10.75"))) {
				//count++;
				//StringBuilder builder = new StringBuilder();
				data[0] = d_max;
				data[1] = q_d_max;
				data[2] = f_min;
				data[3] = alpha_f_min;
				data[4] = f_max;
				data[5] = alpha_f_max;
				
				System.out.println("POLY { " + d_max + " ; " + q_d_max + " ; " + f_min + " ; " + alpha_f_min + " ; " + f_max + " ; " + alpha_f_max + " } ");
			
				start = false;
			}
			j++;
		}
		
		return true;
	}

	public static boolean isCorrect(File file, Message msg) {
		try {
			@SuppressWarnings("resource")
			Scanner sc = new Scanner(file);
			boolean start = false;
			int j = 0;

			while (sc.hasNext()) {

				String[] line = sc.nextLine().split(";");
				if (!start && line.length > 0 && line[0].equals("-10")) {
					start = true;
					j = 0;
				}
				float[] nums = new float[6];
				if (j < 84 && start) {
					for (int i = 0; i < 6; i++) {
						if (!line[i].equals("?")) {
							String temp = line[i].replace(",", ".");
							float num = Float.parseFloat(temp);
							nums[i] = num;
						}
					}
				}
				j++;
			}
		} catch (Exception e) {
			msg.message = "Nastala je greska, pokusajte s drugim fajlom";
			e.printStackTrace();
			return false;
		}

		msg.message = "All good";
		return true;
	}

	public void readData(String fileName, int type) {
		Scanner sc;
		System.out.println("Toi toi");
		try {
			sc = new Scanner(new File(fileName));
			// read header
			sc.nextLine();
			System.out.println("reading file " + fileName);
			while (sc.hasNext()) {
				String[] line = sc.nextLine().replace(",", ".").split(";");
				if (line.length < 7)
					continue;
				matrix[matrix_i][0] = Float.parseFloat(line[0]);
				matrix[matrix_i][1] = Float.parseFloat(line[1]);
				matrix[matrix_i][2] = Float.parseFloat(line[2]);
				matrix[matrix_i][3] = Float.parseFloat(line[3]);
				matrix[matrix_i][4] = Float.parseFloat(line[4]);
				matrix[matrix_i][5] = Float.parseFloat(line[5]);
				types[matrix_i] = type;

				mean[0] += matrix[matrix_i][0];
				mean[1] += matrix[matrix_i][1];
				mean[2] += matrix[matrix_i][2];
				mean[3] += matrix[matrix_i][3];
				mean[4] += matrix[matrix_i][4];
				mean[5] += matrix[matrix_i][5];

				min[0] = Math.min(min[0], matrix[matrix_i][0]);
				min[1] = Math.min(min[1], matrix[matrix_i][1]);
				min[2] = Math.min(min[2], matrix[matrix_i][2]);
				min[3] = Math.min(min[3], matrix[matrix_i][3]);
				min[4] = Math.min(min[4], matrix[matrix_i][4]);
				min[5] = Math.min(min[5], matrix[matrix_i][5]);

				max[0] = Math.max(max[0], matrix[matrix_i][0]);
				max[1] = Math.max(max[1], matrix[matrix_i][1]);
				max[2] = Math.max(max[2], matrix[matrix_i][2]);
				max[3] = Math.max(max[3], matrix[matrix_i][3]);
				max[4] = Math.max(max[4], matrix[matrix_i][4]);
				max[5] = Math.max(max[5], matrix[matrix_i][5]);

				matrix_i++;
				count++;
			}
		} catch (FileNotFoundException ex) {
			ex.printStackTrace();

		}

	}

	public void normalize(ArrayList<String> data,
			ArrayList<String> plottableData) {
		for (int i = 0; i < mean.length; i++) {
			mean[i] /= count;
			std[i] = max[i] - min[i];
		}

		//for (int i = 0; i < 6; i++) {
		//	System.out.println(mean[i] + " " + std[i]);
		//}

		for (int i = 0; i < count; i++) {
			//System.out.println(Arrays.toString(matrix[i]) + " " + types[i]);
			for (int jz = 0; jz < 6; jz++) {
				matrix[i][jz] = (matrix[i][jz] - mean[jz])
						/ (importance_factor[jz] * std[jz]);
			}
			//System.out.println(Arrays.toString(matrix[i]) + " " + types[i]);

		}

		//System.out.println("count = " + count);
		for (int i = 0; i < count; i++) {
			StringBuilder builder = new StringBuilder();
			for (int j = 0; j < 6; j++) {
				builder.append(matrix[i][j] + ",");
			}
			
			//int num = 0;
			for (int j = 0; j < 6; j++) {
				for (int k = j; k < 6; k++) {
					builder.append(matrix[i][j] * matrix[i][k] + ",");
					//num++;
				}
			}

			builder.append(types[i]);
			data.add(builder.toString());

			StringBuilder sbuilder = new StringBuilder();
			sbuilder.append(matrix[i][4] + ",");
			sbuilder.append(matrix[i][5] + ",");
			sbuilder.append(types[i]);
			plottableData.add(sbuilder.toString());
		}
	}

	public void normalize(float[] data) {

		for (int i = 0; i < data.length; i++) {
			data[i] = (data[i] - mean[i]) / (importance_factor[i] * std[i]);
		}
	}

	public float[] createInput(float[] data) {
		float[] result = new float[27];
		int count = 0;
		for (int i = 0; i < data.length; i++) {
			result[i] = data[i];
			count++;
		}

		for (int j = 0; j < 6; j++) {
			for (int k = j; k < 6; k++) {
				result[count++] = data[j] * data[k];
			}
		}

		return result;
	}

	public StringBuilder prepareData(float[] data) {
		StringBuilder builder = new StringBuilder();
		for(int i = 0; i < data.length; i++){
			if(i > 0)
				builder.append(",");
			builder.append(data[i]);
		}
		return builder;
	}
	
	static public class ClassificationResult
	{
		public boolean success = false;
		public int type = -1;
		public int catCount = 3;
		
		public static final int STATS_COUNT = 6;
		public static final String[] STAT_NAMES = 
			{
				"Accuracy",
				"Sensitivity",
				"Specificity",
				"Geometric mean of sensitivity and specificity",
				"Precision",
				"F-Measure"
			};
		
		static public class Stats
		{
			public float accuracy = 0.0f;
			public float sensitivity = 0.0f;
			public float specificity = 0.0f;
			public float geomMean = 0.0f;
			public float precision = 0.0f;
			public float fMeasure = 0.0f;
		}
		
		//dpublic Stats[] stats = null;
	}
	
	public static ClassificationResult parseResluts()
	{
		return parseResluts(SCRIPTS_BASE + "result_classification.txt"); //, 
				//"octave_scripts/result_validation.txt");
	}
	
	public static ClassificationResult parseResluts(String fnType) //, String fnStats)
	{
		try
		{
			Scanner s1 = new Scanner(new File(fnType));
			//Scanner s2 = new Scanner(new File(fnStats));
			
			ClassificationResult cr = parseResults(s1);
			
			s1.close();
			//s2.close();
			return cr;
		} catch (FileNotFoundException e)
		{
			System.out.println("Error opening files");
			return new ClassificationResult();
		}
	}
	
	public static ClassificationResult.Stats[] parseTrainingStats()
	{
		return parseTrainingStats(SCRIPTS_BASE + "result_validation.txt"); 
	}
	
	public static ClassificationResult.Stats[] parseTrainingStats(String fileName)
	{
		try
		{
			Scanner s = new Scanner(new File(fileName));
			ClassificationResult.Stats[] stats = parseTrainingStats(s);
			s.close();
			return stats;
		} 
		catch (FileNotFoundException e)
		{
			return null;
		}
	}
	
	public static ClassificationResult.Stats[] parseTrainingStats(Scanner sStats)
	{
		ArrayList<ClassificationResult.Stats> statList = 
				new ArrayList<ClassificationResult.Stats>();
			
		while(sStats.hasNext())
		{
			String line = sStats.nextLine();
			if(line.trim().length() == 0) break;
			
			ClassificationResult.Stats stats = new ClassificationResult.Stats();
			for(int i = 0; i < ClassificationResult.STATS_COUNT; ++i)
			{
				//String name = (line != null) ? line : sStats.nextLine();
				String name = sStats.nextLine();
				//line = null;
				name = name.trim();
				if(name.equals(ClassificationResult.STAT_NAMES[i]))
				{
					float val = sStats.nextFloat(); 
					sStats.nextLine();
					switch(i)
					{
					case 0: stats.accuracy = val; break;
					case 1: stats.sensitivity = val; break;
					case 2: stats.specificity = val; break;
					case 3: stats.geomMean = val; break;
					case 4: stats.precision = val; break;
					case 5: stats.fMeasure = val; break;
					}
				}
				else
				{
					if(name.length() == 0 || !sStats.hasNext())
					{
						break;
					}
					System.out.println("Unknown stat \"" + name + "\"");
				}
			}
			statList.add(stats);
		}
		
		//cr.catCount = statList.size();
		//System.out.println("catCount = " + cr.catCount);
		//cr.stats = new ClassificationResult.Stats[statList.size()];
		//statList.toArray(cr.stats);
		
		ClassificationResult.Stats[] statArray = new ClassificationResult.Stats[statList.size()];
		statList.toArray(statArray);
		return statArray;
	}
	
	public static ClassificationResult parseResults(Scanner sType) //, Scanner sStats)
	{
		ClassificationResult cr = new ClassificationResult();
		
		double type = sType.nextDouble();
		cr.type = (int)type;
		
		cr.success = true;
		
		return cr;
	}
	
	public static ClassificationResult classifyByParameters(String fileName)
	{
		try
		{
			Scanner s = new Scanner(new File(fileName));
			ClassificationResult result = classifyByParameters(s);
			s.close();
			return result;
		} 
		catch (FileNotFoundException e)
		{
			System.out.println("Error loading CSV \"" + fileName + "\"");
			return new ClassificationResult();
		}
	}
	
	public static ClassificationResult classifyByParameters(Scanner s)
	{
		float[] params = new float[6];
		if(doPolynomialForOne(s, params))
			return classifyByParameters(params);
		else
			return new ClassificationResult();
	}
	
	public static ClassificationResult classifyByParameters(float[] p)
	{
		if(p.length != 6)
		{
			System.out.println("lolwut?");
			return null;
		}
		else
		{
			return classifyByParameters(p[0], p[1], p[2], p[3], p[4], p[5]);
		}
	}
	
	public static ClassificationResult classifyByParameters(float d_max, float q, float f_alpha_min, 
			float alpha_min, float f_alpha_max, float alpha_max)
	{
		//File file = new File(".");
		//System.out.println(file.getAbsolutePath());
		ArrayList<String> data = new ArrayList<>();
		ArrayList<String> plottableData = new ArrayList<>();
		
		InstancedParser ip = new InstancedParser();
		
		ip.doPolynomial("data\\Fraclac rezultati za pluca.csv", 1, data, plottableData);
		ip.doPolynomial("data\\Fraclac rezultati za bubreg.csv", 2, data, plottableData);
		ip.doPolynomial("data\\Fraclac rezultati za dojku.csv", 3, data, plottableData);
		ip.normalize(data, plottableData);
		

		CSVToMatrix.writeToFiles(SCRIPTS_BASE + "karcinom.m", SCRIPTS_BASE + "testkarcinom.m", data, plottableData);
		
		System.out.println("CLASSIFY { " + d_max + " ; " + q + " ; " + f_alpha_min + " ; " + alpha_min + " ; " + f_alpha_max + " ; " + alpha_max + " } ");
		
		// sacuva parametre
		float paramData[] = new float[6];
//					StringBuilder sb = new StringBuilder();
		paramData[0] = d_max;
		paramData[1] = q;
		paramData[2] = f_alpha_min;
		paramData[3] = alpha_min;
		paramData[4] = f_alpha_max;
		paramData[5] = alpha_max;
		
		//System.out.println(Arrays.toString(paramData));
		ip.normalize(paramData);
		
		//System.out.println(Arrays.toString(paramData));
		paramData = ip.createInput(paramData);
		
		//System.out.println(Arrays.toString(paramData));
		StringBuilder sb = ip.prepareData(paramData);
		
		System.out.println(Arrays.toString(paramData));
//					sb.append(d_max.getText() + ",");
//					sb.append(q.getText() + ",");
//					sb.append(f_alpha_min.getText() + ",");
//					sb.append(alpha_min.getText() + ",");
//					sb.append(f_alpha_max.getText() + ",");
//					sb.append(alpha_max.getText());
		String toString = sb.toString();
		System.out.println(toString);
		
		File file = new File(SCRIPTS_BASE + "onekarcinom.m");
		try {
			PrintWriter writer = new PrintWriter(file);
			//writer.append(toString);
			writer.print(toString);
			
			System.out.println("OK = " + toString);
			
			writer.close();
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}

		File res1 = new File(SCRIPTS_BASE + "result_classification.txt");
		if(res1.exists()) res1.delete();
		
		//File res2 = new File("octave_scripts/result_trening.txt");
		//if(res2.exists()) res2.delete();
		
		//File res3 = new File("octave_scripts/result_validation.txt");
		//if(res3.exists()) res3.delete();
		
		// pozove komandu
		// TODO fixme, ovo ne radi kako treba
		double result = CommandRunner.runCommand(OCTAVE_PATH + " metastaze_one.m no_pause no_graphic");
		//result = CommandRunner.runCommand(OCTAVE_PATH + " metastaze_predictor.m no_pause no_graphic");
		
		System.out.println("Result = " + result);
		
		ClassificationResult cr = parseResluts();
		System.out.println("Type = " + cr.type);
		//System.out.println("Accuracy = " + cr.stats[cr.type - 1].accuracy);
		// read result
		//Frame.getFrame().setReadyStatePanelVisible(false);
		//Frame.getFrame().setResultPanelVisible(true);
		System.out.println("Success!");
		System.out.println();
		
		return cr;
	}
	
	public void writeToFiles(String trainingFile, String validationFile,
			ArrayList<String> data, ArrayList<String> plottableData) {
		Collections.shuffle(data);
		System.out.println(data.size());
		int n = 3 * data.size() / 4;
		int m = 1 * data.size() / 4;
		System.out.println(n + ", " + m);
		System.out.println(data.get(0).split(",").length);
		try {
			@SuppressWarnings("resource")
			PrintWriter writer = new PrintWriter(new File(trainingFile));
			for (int i = 0; i < n; i++) {
				writer.println(data.get(i));
			}
			writer.close();

			PrintWriter writer1 = new PrintWriter(new File(validationFile));
			for (int i = n; i < data.size(); i++) {
				writer1.println(data.get(i));
			}
			writer1.close();

			PrintWriter writer2 = new PrintWriter(new File("data.txt"));
			Collections.shuffle(plottableData);
			for (int i = 0; i < plottableData.size() / 4; i++) {
				writer2.println(plottableData.get(i));
			}
			writer2.close();

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args)
	{
		//InstancedParser ip = new InstancedParser();
		
		System.out.println("START");
		
		/*
		ArrayList<String> data = new ArrayList<>();
		ArrayList<String> plottableData = new ArrayList<>();
		ip.doPolynomial("data\\Fraclac rezultati za pluca.csv", 1, data, plottableData);
		ip.doPolynomial("data\\Fraclac rezultati za bubreg.csv", 2, data, plottableData);
		ip.doPolynomial("data\\Fraclac rezultati za dojku.csv", 3, data, plottableData);
		ip.normalize(data, plottableData);

		CSVToMatrix.writeToFiles("parse-1.m", "parse-2.m", data, plottableData);
		*/
		
		classifyByParameters(2.7278f, -6.5f, 0.8921f, 3.0103f, 1.8892f, 2.0417f);
		
		System.out.println("END");

	}

}
